package neuron;

import java.util.ArrayList;
import java.util.List;


public class Dendrite extends AbstractFiber {
	
	public Dendrite(Segment root) {
		this.root = root;
	}
	 
	@Override
	public String toString() {
		return "Dendrite; root " + root;
	}
	/**
	 * Compute straightness = (distance along dendrite) divided by (direct distance from start point to end point) for root segment.
	 * The distance along dendrite is always greater or equal to direct distance between start and end.
	 * If the root segment is straight, then straightness = 1.0, otherwise it will be > 1.0, larger when the dendrite curves more.   
	 * @return
	 */
	public double straightness()
	{
		double pathLen = root.length();
		double directLen = root.lastCoordinate().distanceTo(root.firstCoordinate());
		return pathLen / directLen;
	}
	

	/**
	 * Get assumed apical base path. (soma to tuft via apical)
	 * @return
	 */
	public List<Segment> getApicalBasePath()
	{
		List<Segment> maxPath = root.maxLengthPath();
		List<Segment> apicalBase = root.apicalBasePath(Segment.pathLength(maxPath));
		return apicalBase;
	}
	
	
	/**
	 * Construct new dendrites to represent obliques. 
	 * 
	 * @return
	 */
	public List<Dendrite> getObliqueDendrites()
	{
		List<Dendrite> lst = new ArrayList<Dendrite>();
		for (Segment s : getObliqueRoots()) {
			lst.add(new Dendrite(s));
		}
		return lst;
	}
	
	/**
	 * Get list of segments where oblique dendrites start. 
	 * Use only for apical dendrites. 
	 * @return
	 */
	public List<Segment> getObliqueRoots()
	{
		List<Segment> maxPath = root.maxLengthPath();
		double maxLen = Segment.pathLength(maxPath);
		List<Segment> curPath = new ArrayList<Segment>();
		List<Segment> obliqueRoots = new ArrayList<Segment>();
		
		for (Segment s : maxPath) {
			curPath.add(s);
			double curLen = Segment.pathLength(curPath);
			if (curLen > 0.50 * maxLen) break; // 50% of max reached

			// Get branches from current segment. 
			// Assume that shorter branch is oblique (we could also look at the angle, maybe?)
			List<Segment> branches = s.getBranches();
			if (!branches.isEmpty() && branches.size() >= 2) {
				double b1Len = branches.get(0).segMaxLen();
				double b2Len = branches.get(1).segMaxLen();
				double b3Len = 0; 
				if (branches.size() >= 3) b3Len = branches.get(2).segMaxLen();
				double maxBranchLen = Math.max(b1Len, b2Len);
				maxBranchLen = Math.max(maxBranchLen, b3Len);

				// Which is shortest? We assume that the longest is apical 
				// continuation, and shortest are oblique
				if (b1Len < maxBranchLen/2) obliqueRoots.add(branches.get(0));
				if (b2Len < maxBranchLen/2) obliqueRoots.add(branches.get(1));
				if (b3Len != 0 && b3Len < maxBranchLen/2) obliqueRoots.add(branches.get(2));
			}
		}
		return obliqueRoots;
	}

	public List<Segment> getTuftRoot()
	{
		List<Segment> obliqueRoots = getObliqueRoots();
		List<Segment> maxPath = root.maxLengthPath();
//		System.out.println("GET TUFT ROOT: OBLIQUES : " + obliqueRoots.size());
		
		for (Segment s : maxPath) {
			
//			System.out.println("SEG " + s);
			
			// if segment branches, and all obliques are encountered, return
			if (obliqueRoots.size() == 0 && s.getBranches().size() >= 2) {
//				System.out.println("NO OBL, SEG BRANCHES: " + s.getBranches().size());
				return s.getBranches();
			}

			// look at branches, remove obliques from list
			List<Segment> branches = s.getBranches();
			for (Segment bs : branches) {
				if (obliqueRoots.contains(bs)) {
					obliqueRoots.remove(bs);
//					System.out.println("OBL " + bs + ", left: " + obliqueRoots.size());
				}
			}
		}
		
		return null; // algorithm didn't find tuft root
	}

	public Dendrite getTuftDendrite()
	{
		Segment s = new Segment();
		for (Segment b : getTuftRoot()) { s.addBranch(b); }
		return new Dendrite(s);
	}
	
	public List<Dendrite> getTuftDendriteAsList()
	{
		List<Dendrite> lst = new ArrayList<Dendrite>();
		lst.add(getTuftDendrite());
		return lst;
	}
	
	/**
	 * Return list of segments that start tuft. 
	 * @param thresholdDistance - y coordinate where tuft starts, e.g. 10% of pia-wm distance
	 * @return
	 */
	private List<Segment> getTuftRoots(Segment start, double thresholdDistance)
	{
		List<Segment> tuftRoots = new ArrayList<Segment>();
		
		// this starts tuft?
		if (start.firstCoordinate().y > thresholdDistance) {
			tuftRoots.add(start);
			return tuftRoots;
		}
		
		// check children
		for (Segment child : start.getBranches()) {
			tuftRoots.addAll(getTuftRoots(child, thresholdDistance));
		}
		return tuftRoots;
	}
	
	/**
	 * Return list of segments that start tuft. 
	 * @param thresholdDistance - distance where tuft starts, e.g. 10% of pia-wm distance
	 * @return
	 */
	public List<Segment> getTuftRoots(double thresholdDistance)
	{
		List<Segment> tuftRoots = getTuftRoots(root, thresholdDistance);
		return tuftRoots;
	}
	
	/**
	 * Return max y coordinate of dendrite
	 * 
	 */
	public double maxY()
	{
		double maxY = Double.MIN_VALUE;
		for (Coord c : getAllCoordinates()) {
			if (c.y > maxY) maxY = c.y;
		}
		return maxY;
	}
}
